﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using AllenHung.FeedReader.Resources;
using Windows.Phone.Speech.VoiceCommands;
using System.IO;
using System.ServiceModel.Syndication;
using System.Xml;
using Microsoft.Phone.Tasks;
using Windows.Phone.Speech.Synthesis;
using Windows.Phone.Speech.Recognition;
using Windows.Foundation;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics;
using System.Windows.Threading;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Xml.Linq;

namespace AllenHung.FeedReader
{
    public partial class ArticleListPage : PhoneApplicationPage
    {
        internal const string UriKey = "FUri";
        private string feedURI;
        String feedTitle;
        private List<SyndicationItem> currentFeeds;

        private int tickCount = 0;
        private DispatcherTimer speechTimer;
        internal SpeechSynthesizer synthesizer;                             // The speech synthesizer (text-to-speech, TTS) object
        internal SpeechRecognizer recognizer;                               // The speech recognition object
        private bool recognizeInProgress = false;
        private int skipCounter = 0;
        private static string[] ArticleListPageVocabulary = new string[]{
            "next",
            "previous",
            "select"
        };
        private Dictionary<string, Action> ActionMap = null;
        private static int CurrentIndex = -1;
        private static bool ExecutingCommand = false;
        private static bool AutoSelectionChange = false;

        public ArticleListPage()
        {
            InitializeComponent();

            synthesizer = new SpeechSynthesizer();
            recognizer = new SpeechRecognizer();
            recognizer.Grammars.AddGrammarFromList("ArticleListPage", ArticleListPageVocabulary);
            ActionMap = new Dictionary<string, Action>();
            ActionMap.Add(ArticleListPageVocabulary[0], this.NextArticle);
            ActionMap.Add(ArticleListPageVocabulary[1], this.PreviousArticle);
            ActionMap.Add(ArticleListPageVocabulary[2], this.SelectArticle);
        }

        // This method sets up the feed and binds it to our ListBox. 
        private void UpdateFeedArticleList(string feedXML)
        {
            
            // Load the feed into a SyndicationFeed instance.
            StringReader stringReader = new StringReader(feedXML);
            XmlReader xmlReader = XmlReader.Create(stringReader);
            SyndicationFeed currentFeed = SyndicationFeed.Load(xmlReader);
            FeedLabel.Text = currentFeed.Title.Text.ToUpper();
     
            // In Windows Phone OS 7.1 or later versions, WebClient events are raised on the same type of thread they were called upon. 
            // For example, if WebClient was run on a background thread, the event would be raised on the background thread. 
            // While WebClient can raise an event on the UI thread if called from the UI thread, a best practice is to always 
            // use the Dispatcher to update the UI. This keeps the UI thread free from heavy processing.
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                // Bind the list of SyndicationItems to our ListBox.
                feedListBox.ItemsSource = currentFeed.Items;

            });
            currentFeeds = currentFeed.Items.ToList<SyndicationItem>();
        }

        // Click handler that runs when the 'Load Feed' or 'Refresh Feed' button is clicked. 
        public void loadFeedArticleList()
        {
            FeedList feedList = new FeedList();

            // WebClient is used instead of HttpWebRequest in this code sample because 
            // the implementation is simpler and easier to use, and we do not need to use 
            // advanced functionality that HttpWebRequest pq,o $rovides, such as the ability to send headers.
            WebClient webClient = new WebClient();

            // Subscribe to the DownloadStringCompleted event prior to downloading the RSS feed.
            webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);

            // Download the RSS feed. DownloadStringAsync was used instead of OpenStreamAsync because we do not need 
            // to leave a stream open, and we will not need to worry about closing the channel. 
            webClient.DownloadStringAsync(new System.Uri(feedURI));
        }

            protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e);
            Debug.WriteLine("OnNavigatedFrom ArticleListPage");
            App.CurrentPage = 0;
            
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            Debug.WriteLine("OnNavigatedTo ArticleListPage");
            App.CurrentPage = 2;
            if (!App.SpeechOn)
            {
                BitmapImage muteImage = new BitmapImage(new Uri("/Assets/mute.png", UriKind.Relative));
                SpeechButtonImage.Source = muteImage;
                SpeechStatusText.Background = (Brush)Application.Current.Resources["DisabledBrush"];
                SpeechStatusText.BorderBrush = (Brush)Application.Current.Resources["DisabledBrush"];
                SpeechStatusText.Content = "Disabled";
            }
            else
            {
                CurrentIndex = -1;
                speechTimer = new System.Windows.Threading.DispatcherTimer();
                speechTimer.Tick += new EventHandler(speechTimer_Tick);
                speechTimer.Interval = new TimeSpan(500 * TimeSpan.TicksPerMillisecond);
                speechTimer.Start();
                BitmapImage muteImage = new BitmapImage(new Uri("/Assets/microphone.png", UriKind.Relative));
                SpeechButtonImage.Source = muteImage;
                SpeechStatusText.Background = (Brush)Application.Current.Resources["FeedBrush"];
                SpeechStatusText.BorderBrush = (Brush)Application.Current.Resources["FeedBrush"];
                SpeechStatusText.Content = "Listening ...";
                Deployment.Current.Dispatcher.BeginInvoke(async () =>
                {
                    await synthesizer.SpeakTextAsync("Say \"next\" to focus on an article");
                });
            }
            string entry = @App.currentFeedURI;
            if(entry == null){
                entry = "http://sports.espn.go.com/espn/rss/news";
            }
            this.feedURI = entry;
            NavigationContext.QueryString.TryGetValue(UriKey, out entry);
            

            // First, check whether the feed is already saved in the page state.
            if (this.State.ContainsKey("feed"))
            {
                // Get the feed again only if the application was tombstoned, which means the ListBox will be empty.
                // This is because the OnNavigatedTo method is also called when navigating between pages in your application.
                // You would want to rebind only if your application was tombstoned and page state has been lost. 
                if (feedListBox.Items.Count == 0)
                {
                    UpdateFeedArticleList(State["feed"] as string);
                }
            }

            loadFeedArticleList();

        }

        // Event handler which runs after the feed is fully downloaded.
        private void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                {
                    // Showing the exact error message is useful for debugging. In a finalized application, 
                    // output a friendly and applicable string to the user instead. 
                    MessageBox.Show(e.Error.Message);
                });
            }
            else
            {
                // Save the feed into the State property in case the application is tombstoned. 
                this.State["feed"] = e.Result;

                UpdateFeedArticleList(e.Result);
            }
        }

        // The SelectionChanged handler for the feed items 
        private void feedListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (AutoSelectionChange)
            {
                AutoSelectionChange = false;
                return;
            }
            ListBox listBox = sender as ListBox;

            if (listBox != null && listBox.SelectedItem != null)
            {
                // Get the SyndicationItem that was tapped.
                SyndicationItem sItem = (SyndicationItem)listBox.SelectedItem;
                RssTextTrimmer rstt = new RssTextTrimmer();
               // string content ="";
               App.article = rstt.Convert(sItem.Summary.Text, null, null, null).ToString();
                //content = sItem.ElementExtensions.ReadElementExtensions<string>("encoded", "http://purl.org/rss/1.0/modules/content/").ElementAt<string>(0).ToString();
               // content = ((TextSyndicationContent)sItem.Content).Text;
               // App.article = rstt.Convert(content, null, null, null).ToString();

                App.articleTitle = sItem.Title.Text;

                App.syndItems.Clear();

                for(int i = listBox.SelectedIndex +1;  i < listBox.Items.Count; i++){
                    App.syndItems.Add((SyndicationItem)listBox.Items.ElementAt(i));
                }

                NavigationService.Navigate(new Uri("/ArticlePage.xaml", UriKind.Relative));
            }
        }

        private async void speechTimer_Tick(object sender, EventArgs e)
        {
            tickCount = (tickCount + 1) % 10000;
            int curTick = tickCount;
            if (!recognizeInProgress)
            {
                Debug.WriteLine(curTick + " Start recognizing");
                skipCounter = 0;
                recognizeInProgress = true;
                try
                {// Start recognition.
                    SpeechRecognitionResult recoResult = await recognizer.RecognizeAsync();
                    if (App.CurrentPage != 2)
                    {
                        Debug.WriteLine(curTick + " party over page navigated away");
                        return;
                    }
                    // Check to see if speech input was rejected and prompt the user.
                    if (recoResult.TextConfidence == SpeechRecognitionConfidence.Rejected)
                    {
                        Debug.WriteLine(curTick + " Rejected silence");
                    }

                    // Check to see if speech input was recognized with low confidence and prompt the user to speak again.
                    else if (recoResult.TextConfidence == SpeechRecognitionConfidence.Low ||
                        recoResult.TextConfidence == SpeechRecognitionConfidence.Medium)
                    {
                        if (ExecutingCommand)
                        {
                            Debug.WriteLine(curTick + " low confidence someone already take care of this");
                        }
                        else
                        {
                            Debug.WriteLine(curTick + " Sorry I didn't get that");
                            SetSpeechStatusText("Sorry I didn't get that");
                            await synthesizer.SpeakTextAsync("Sorry I didn't get that");
                        }
                    }

                    // Check to see if speech input was recognized and confirm the result.
                    else if (recoResult.TextConfidence == SpeechRecognitionConfidence.High)
                    {
                        if (ExecutingCommand)
                        {
                            Debug.WriteLine(curTick + " someone already take care of this");
                        }
                        else
                        {
                            Debug.WriteLine(curTick + " You say " + recoResult.Text);
                            SetSpeechStatusText("You say " + recoResult.Text);
                            if (ActionMap.ContainsKey(recoResult.Text))
                            {
                                ExecutingCommand = true;
                                Action act = ActionMap[recoResult.Text];
                                act();
                                BackgroundWorker backgroundWorker = new BackgroundWorker();
                                backgroundWorker.DoWork += new DoWorkEventHandler(ResetExecutingCommandStatus);
                                backgroundWorker.RunWorkerAsync();
                            }
                            else
                            {
                                await synthesizer.SpeakTextAsync(recoResult.Text + " is an unknown command. Say \"next\", \"previous\", or \"select\"");
                            }
                        }
                    }
                    recognizeInProgress = false;
                    Debug.WriteLine(curTick + " Recognizing done");
                }
                catch (Exception exc)
                {
                    Debug.WriteLine(curTick + " HRESULT: " + exc.HResult + " " + exc.StackTrace);
                    Debug.WriteLine(curTick + " HRESULT: " + exc.HResult + " " + exc.Message);
                }
            }
            else
            {
                if (++skipCounter == 10)
                {
                    Debug.WriteLine(curTick + " Stale recognizer");
                    recognizer = new SpeechRecognizer();
                    recognizer.Grammars.AddGrammarFromList("MainPage", ArticleListPageVocabulary);
                    recognizeInProgress = false;
                }
                Debug.WriteLine(curTick + " Skip recognizing");
            }
        }

        private void SetSpeechStatusText(string value)
        {
            SpeechStatusText.Content = value;
            BackgroundWorker backgroundWorker = new BackgroundWorker();
            backgroundWorker.DoWork += new DoWorkEventHandler(DelayResetSpeechStatusText);
            backgroundWorker.RunWorkerAsync();
        }

        private void DelayResetSpeechStatusText(object sender, DoWorkEventArgs e)
        {
            Thread.Sleep(8000);
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                SpeechStatusText.Content = AppResources.listening;
            }
            );
        }

        private void ResetExecutingCommandStatus(object sender, DoWorkEventArgs e)
        {
            Thread.Sleep(2000);
            ExecutingCommand = false;
        }

        private async void NextArticle()
        {
            String newCur = null;
            int count = currentFeeds.Count;
            if (CurrentIndex < 0 || CurrentIndex >= count)
            {
                CurrentIndex = 0;
            }
            else
            {
              //  UnfocusCurrentItem();
                CurrentIndex = (CurrentIndex + 1) % count;
            }
            newCur = FocusCurrentItem();
            await synthesizer.SpeakTextAsync("Move to next article. Focus on " + newCur);
        }

        private async void PreviousArticle()
        {
            String newCur = null;
            int count = currentFeeds.Count;
            if (CurrentIndex < 0 || CurrentIndex >= count)
            {
                CurrentIndex = count - 1;
            }
            else
            {
              //  UnfocusCurrentItem();
                CurrentIndex = (CurrentIndex - 1 + count) % count;
                Debug.WriteLine("PreviousFeedSrc current index " + CurrentIndex);
            }
            newCur = FocusCurrentItem();
            await synthesizer.SpeakTextAsync("Move to previous article. Focus on " + newCur);
        }

        private async void SelectArticle()
        {
            if (CurrentIndex < 0 || CurrentIndex >= currentFeeds.Count)
            {
                await synthesizer.SpeakTextAsync("No item focused. Say \"Next\" to select an article");
            }
            else
            {
                await synthesizer.SpeakTextAsync("Navigate to article");
                SyndicationItem sItem = currentFeeds[CurrentIndex];
                RssTextTrimmer rstt = new RssTextTrimmer();
                App.article = rstt.Convert(sItem.Summary.Text, null, null, null).ToString();
                App.articleTitle = sItem.Title.Text;
                App.syndItems.Clear();
                for (int i = CurrentIndex + 1; i < currentFeeds.Count; i++)
                {
                    App.syndItems.Add(currentFeeds[i]);
                }
                NavigationService.Navigate(new Uri("/ArticlePage.xaml", UriKind.Relative));
            }
        }

        //private void UnfocusCurrentItem()
        //{
        //    AutoSelectionChange = true;
        //    feedListBox.SelectedIndex = CurrentIndex;
        //    feedListBox.ScrollIntoView(feedListBox.SelectedItem);
        // //   ListBoxItem item = feedListBox.ItemContainerGenerator.ContainerFromIndex(CurrentIndex) as ListBoxItem;
        //    ListBoxItem item = feedListBox.SelectedItem as ListBoxItem;
        //    Debug.WriteLine("Get here a");
        //  //  StackPanel stackItem = item.FindName("feedItem") as StackPanel;
        //    StackPanel stackItem = App.FindFirstElementInVisualTree<StackPanel>(item);
        //    stackItem.Background = null;
        //}

        private String FocusCurrentItem()
        {
            AutoSelectionChange = true;
            feedListBox.SelectedIndex = CurrentIndex;
            feedListBox.ScrollIntoView(feedListBox.SelectedItem);
        //    ListBoxItem item = feedListBox.SelectedItem as ListBoxItem;
            Debug.WriteLine("Get here b " + (feedListBox.SelectedItem is ListBoxItem) + " or " + (feedListBox.SelectedItem is SyndicationItem));
//            StackPanel stackItem = item.FindName("feedItem") as StackPanel;
            //StackPanel stackItem = App.FindFirstElementInVisualTree<StackPanel>(item);
            //if (stackItem == null)
            //{
            //    Debug.WriteLine("stackItem is null return");
            //}
            //stackItem.Background = (Brush)Application.Current.Resources["FeedBrush"];
            //TextBlock title = App.FindFirstElementInVisualTree<TextBlock>(stackItem);
            return (feedListBox.SelectedItem as SyndicationItem).Title.Text;
        }
    }
}